namespace SubhadraSolutions.Sharp.Logging
{
    using SubhadraSolutions.Sharp.Utils;
    using SubhadraSolutions.Sharp.Utils.Collections.Concurrent;
    using SubhadraSolutions.Sharp.Utils.Collections.Generic;
    using SubhadraSolutions.Sharp.Utils.Diagnostics.Performance;
    using SubhadraSolutions.Sharp.Utils.Diagnostics.Performance.Dtos;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;

    public sealed class LogStoreItemsCacheManager : AbstractDisposable
    {
        #region Fields

        private static readonly LogStoreItemsCacheManager _instance = new LogStoreItemsCacheManager();

        private readonly LockFactory _lockFactory = new LockFactory();

        private readonly CompositeDictionary<string, ProcessSettingsInfo> _settingsDictionary =
            new CompositeDictionary<string, ProcessSettingsInfo>(true);

        private readonly CompositeDictionary<string, SlidingWindow<LogStoreItem>> _windowDictionary =
            new CompositeDictionary<string, SlidingWindow<LogStoreItem>>(false);

        #endregion Fields

        #region Constructors

        private LogStoreItemsCacheManager()
        {
            SlidingWindowSize = 60;
        }

        #endregion Constructors

        #region Properties

        public static LogStoreItemsCacheManager Instance
        {
            get { return _instance; }
        }

        public int SlidingWindowSize
        {
            get;
            set;
        }

        #endregion Properties

        #region Methods

        public List<string> GetApplicationNames(string machineName)
        {
            return GetSubKeys(machineName);
        }

        public List<LogStoreItem> GetLogStoreItems(string machineName, string applicationName,
            string environment,
            string category)
        {
            try
            {
                SlidingWindow<LogStoreItem> window;
                if (_windowDictionary.TryGetValue(out window, machineName, applicationName,
                    environment, category))
                {
                    return window.GetAllItems().ToList();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            return new List<LogStoreItem>(0);
        }

        public List<string> GetEnvironmentNames(string machineName, string applicationName)
        {
            return GetSubKeys(machineName, applicationName);
        }

        public List<string> GetMachineNames()
        {
            return GetSubKeys(new string[] { });
        }

        public bool GetShouldPublish(string machineName, string applicationName, string environment)
        {
            ProcessSettingsInfo settings;
            if (_settingsDictionary.TryGetValue(out settings, machineName, applicationName,
                environment))
            {
                return settings.ShouldPublish;
            }
            return true;
        }

        public List<string> GetSubKeys(params string[] keys)
        {
            try
            {
                List<string> list = _windowDictionary.GetKeys(keys);
                list.Sort();
                return list;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            return new List<string>(0);
        }

        public void PublishLogStoreItems(List<LogStoreItem> items, ProcessInfo info)
        {
            try
            {
                bool isCreated = false;
                ProcessSettingsInfo settings = _settingsDictionary.GetOrAddAndGet(delegate
                {
                    var sett = new ProcessSettingsInfo { ProcessId = info.ProcessId };
                    isCreated = true;
                    return sett;
                }, info.MachineName, info.ApplicationName, info.Environment);
                lock (settings)
                {
                    if (!isCreated)
                    {
                        if (info.ProcessId != settings.ProcessId &&
                            (info.ProcessId != settings.PreviousProcessId))
                        {
                            settings.PreviousProcessId = settings.ProcessId;
                            settings.ProcessId = info.ProcessId;
                        }
                    }
                    if (settings.PreviousProcessId == null ||
                        settings.PreviousProcessId != info.ProcessId)
                    {
                        foreach (var item in items)
                        {
                            string category = item.Categories.ToString();
                            SlidingWindow<LogStoreItem> window;
                            if (
                                !_windowDictionary.TryGetValue(out window, info.MachineName,
                                    info.ApplicationName, info.Environment,
                                    item.Categories.ToString()))
                            {
                                window = new SlidingWindow<LogStoreItem>(SlidingWindowSize);
                                _windowDictionary.AddOrUpdate(window,
                                    string.Intern(info.MachineName),
                                    string.Intern(info.ApplicationName),
                                    string.Intern(info.Environment),
                                    string.Intern(category));
                            }
                            window.Add(item);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
        }

        public void SetShouldPublish(string machineName, string applicationName, string environment,
            bool value)
        {
            ProcessSettingsInfo settings;
            if (_settingsDictionary.TryGetValue(out settings, machineName, applicationName,
                environment))
            {
                settings.ShouldPublish = value;
            }
        }

        protected override void Dispose(bool disposing)
        {
            _lockFactory.Dispose();
            _windowDictionary.Dispose();
            _settingsDictionary.Dispose();
        }

        #endregion Methods
    }
}